/*
*	Copyright (C) 2014  Julio Montes, Mario Carrillo
*
*	Julio Montes e-mail: imc.coder@gmail.com
*
*	Mario Carrillo e-mail: mario_90ago@live.com.mx
*
*	BSD 3-Clause License
*/

#include "alliance/include/processmgr.h"
#include "alliance/include/project.h"
#include "alliance/include/logmgr.h"
#include "sdk/include/path.h"

#include <wx/ffile.h>

#define ID_PROCESS 9001

namespace alliance
{
    template<> ProcessManager *sdk::Mgr<ProcessManager>::instance = 0;

    ProcessManager::ProcessManager()
        : m_nextTool( 0 )
        , m_isBusy( false )
        , m_project( NULL )
        , m_lastExitCode( 0 )
        , m_caller( NULL )
        , m_process( NULL )
        , m_stopCompile( false )
    {
        Connect( ID_PROCESS, wxEVT_END_PROCESS, wxProcessEventHandler( ProcessManager::OnEndProcess ), NULL, this );
    }

    ProcessManager::~ProcessManager()
    {

    }

    void ProcessManager::CompileProject( Project *project, CallbackFunction cbFunction, void *caller )
    {
        m_project = project;
        m_cbFunction = cbFunction;
        m_nextTool = 0;
        m_isBusy = true;
        m_lastExitCode = 0;
        m_caller = caller;
        m_stopCompile = false;

        LogManager::Get()->ClearLogProject( project );

        ExecuteNextTool();
    }

    bool ProcessManager::OnClose()
    {
        StopCompile();
        return true;
    }

    void ProcessManager::StopCompile()
    {
        m_stopCompile = true;
    }

    void ProcessManager::ExecuteNextTool()
    {
        if( m_stopCompile )
        {
            if( m_cbFunction != NULL )
            {
                (*m_cbFunction)( true, m_nextTool, m_caller );
            }
            m_isBusy = false;
            return;
        }

        const std::vector<Tool> &vTools = m_project->GetTools();
        if( m_nextTool >= vTools.size() )
        {
            if( m_cbFunction != NULL )
            {
                (*m_cbFunction)( false, m_nextTool-1, m_caller );
            }
            m_isBusy = false;
            return;
        }

        //TODO: reimplement
        //check if the infile has been modified, to avoid recompile
        /*wxString inFile = vTools[m_nextTool].GetFileByType( Tool::s_typeInput );
        wxString outFile = vTools[m_nextTool].GetFileByType( Tool::s_typeOutput );
        if( !inFile.IsEmpty() && !outFile.IsEmpty() )
        {
            inFile = m_project->GetDirectory() + inFile;
            outFile = m_project->GetDirectory() + outFile;
            if( wxFileExists( inFile ) && wxFileExists( outFile ) )
            {
                time_t inTime( wxFileModificationTime( inFile ) );
                time_t outTime( wxFileModificationTime( outFile ) );

                if( inTime <= outTime )
                {
                    ++m_nextTool;
                    //!WARNIG:
                    ExecuteNextTool();
                    return;
                }
            }
        }*/

        m_process = new wxProcess( this, ID_PROCESS );
        m_process->Redirect();

        const wxString script( sdk::Path::s_dirConfig + _T( ".script.sh" ) );
        wxFFile ffile( script.c_str(), wxT( "w" ) );
        ffile.Write( wxT( "#!/bin/sh\ncd " ) + m_project->GetDirectory() + wxT( "\n" ) +
            vTools[m_nextTool].GetCommandForFile() +
            wxT( "\nexit $?\n" ) );
        ffile.Close();
        chmod( script.char_str(), S_IXUSR | S_IWUSR | S_IRUSR );
        ++m_nextTool;
        wxExecute( script, wxEXEC_ASYNC, m_process );
    }

    void ProcessManager::OnEndProcess( wxProcessEvent &evt )
    {
        //get input stream (verbose stream)
        wxInputStream *inStream = m_process->GetInputStream();
        wxString bufferIn;
        while( !inStream->Eof() )
            bufferIn+=inStream->GetC();
        bufferIn.RemoveLast();

        //get error stream (error stream)
        wxInputStream *errStream = m_process->GetErrorStream();
        wxString bufferErr;
        while( !errStream->Eof() )
            bufferErr+=errStream->GetC();
        bufferErr.RemoveLast();
        bufferErr = bufferErr.Trim().Trim( false );

        m_process->Detach();
        delete m_process;

        m_lastExitCode = evt.GetExitCode();
        if( m_lastExitCode == 0 )
        {
            LogManager::ImageStatus status;
            if( bufferErr.IsEmpty() )
            {
                status = LogManager::SUCCESS_IMG;
            }
            else
            {
                status = LogManager::WARNING_IMG;
            }

            LogManager::Get()->ShowLog( m_project, m_project->GetTools()[m_nextTool-1],
                status, bufferIn, bufferErr );
            ExecuteNextTool();
        }
        else
        {
            LogManager::Get()->ShowLog( m_project, m_project->GetTools()[m_nextTool-1],
                LogManager::ERROR_IMG, bufferIn, bufferErr );
            (*m_cbFunction)( false, m_nextTool, m_caller );
        }
    }

    void ProcessManager::LaunchViewer( const Project *project, const Tool &viewer )
    {
        const wxString script( sdk::Path::s_dirConfig + _T( ".viewer.sh" ) );
        wxFFile ffile( script.c_str(), wxT( "w" ) );
        ffile.Write( wxT( "#!/bin/sh\ncd " ) + project->GetDirectory() + wxT( "\n" ) +
            viewer.GetCommandForFile() + wxT( "\nexit $?\n" ) );
        ffile.Close();
        chmod( script.char_str(), S_IXUSR | S_IWUSR | S_IRUSR );
        wxExecute( script, wxEXEC_ASYNC );
    }
}
